import {
  Component,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  forwardRef,
  ChangeDetectorRef,
  TemplateRef,
  SimpleChanges,
  Output,
  EventEmitter,
  OnChanges,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { I18NService, InputBoolean, StandardDate, LvDateMode, LvDateRangeTag } from '../core/api';
import { LvPresetRanges, LvDateTimeOption, LvYearsOperate } from './interface';

@Component({
  selector: 'lv-date-picker-panel',
  templateUrl: './date-picker-panel.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef((): typeof DatePickerPanelComponent => DatePickerPanelComponent),
      multi: true,
    },
  ],
  host: {
    '[class]': '"lv-date-picker-panel-host "+ lvPanelClass',
  },
})
export class DatePickerPanelComponent implements ControlValueAccessor, OnChanges {
  @Input() lvOpenObserver;

  @Input() lvIsRange = false;

  @Input() lvPanelClass: string;

  @Input() lvCurrentInput: LvDateRangeTag;

  @Input() @InputBoolean() lvShowNowButton;

  @Input() @InputBoolean() lvNowAsBaseTime;

  @Input() lvPresetRanges: LvPresetRanges[];

  @Input() lvPickerMode: LvDateMode = 'date';

  @Input() @InputBoolean() lvShowTodayButton;

  @Input() lvFooterExtra: TemplateRef<object> | null;

  @Input() lvShowTime: boolean | LvDateTimeOption;

  @Input() lvCalendarCell: TemplateRef<Date> | null;

  @Input() lvCalendarYearCell: TemplateRef<Date> | null;

  @Input() @InputBoolean() lvOnlyShowActiveCell = false;

  @Input() @InputBoolean() lvPanelTitleReadonly = false;

  @Input() lvCalendarMonthCell: TemplateRef<Date> | null;

  @Output() lvActiveDateChange = new EventEmitter<any>();

  @Output() lvCurrentInputChange = new EventEmitter<any>();

  @Input() lvCalendarYearOperate: LvYearsOperate = { show: true, disableChange: false }; // 面板头部年份操作

  @Input() lvCalendarMonthOperate: LvYearsOperate = { show: true, disableChange: false }; // 面板头部月份操作

  @Input() lvDisabledDate: (date: Date, tag: LvDateRangeTag, isPreset?: boolean) => boolean;

  endDate: Date;
  startDate: Date;
  date: StandardDate;
  hoverValue: StandardDate;
  endActiveDate: StandardDate;
  startActiveDate: StandardDate;
  selectedValue: StandardDate[];
  tempSelectedValue: StandardDate[]; // 使用preset预置时间时，用来保存hover到preset上时候selectedValue的值
  constructor(
    private _cdr: ChangeDetectorRef,
    public i18n: I18NService,
  ) {}

  get okButtonStatus(): boolean {
    if (this.lvCurrentInput === 'start') {
      return this.selectedValue[0] ? this.checkDateDisableStatus('start') : true;
    } else {
      return this.selectedValue[1] ? this.checkDateDisableStatus('end') : true;
    }
  }

  cellMouseLeave(): void {
    this.hoverValue = null;
  }

  registerOnChange(fn: any): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this._onTouch = fn;
  }

  onQuickSelectedLeave(): void {
    this.selectedValue = this.tempSelectedValue;
  }

  getObjectKey(obj): string {
    const key = Object.keys(obj);
    return key[0];
  }

  getObjectValue(obj): any {
    const value = Object.values(obj);
    return value[0];
  }

  closeDateRangePicker(): void {
    this.lvOpenObserver?.next(false);
  }

  cellMouseEnter(date): void {
    if (this.lvIsRange) {
      this.hoverValue = date;
    } else {
      this.hoverValue = null;
    }
  }

  onQuickSelectedHover(value): void {
    this.tempSelectedValue = this.selectedValue;
    this.selectedValue = [new StandardDate(value[0]), new StandardDate(value[1])];
  }

  valueChange(value): void {
    if (this.lvIsRange) {
      this.doRangeValueChange(value);
    } else {
      this.date = value;
      this._onChange(value.localDate);
    }
  }

  initDateObject(value): StandardDate {
    if (value === null || value === undefined) {
      return this.lvPickerMode === 'simple' ? new StandardDate(new Date()) : null;
    } else {
      return new StandardDate(value);
    }
  }

  getPresetButtonStatus(dateRange): boolean {
    const start = new StandardDate(dateRange[0]);
    const end = new StandardDate(dateRange[1]);
    return this.isDisabled(start, 'start', true) || this.isDisabled(end, 'end', true);
  }

  quickSelected(value): void {
    this.lvCurrentInput = 'start';
    this.doRangeValueChange(new StandardDate(value[0]), true);
    this.lvCurrentInput = 'end';
    this.doRangeValueChange(new StandardDate(value[1]), true);
    this.closeDateRangePicker();
  }

  checkDateDisableStatus(v?): boolean {
    const tag = v ? v : this.lvCurrentInput;
    if (tag === 'start') {
      return this.isDisabled(this.selectedValue[0], 'start');
    } else if (tag === 'end') {
      return this.isDisabled(this.selectedValue[1], 'end');
    }
    return undefined;
  }

  writeValue(value: any): void {
    if (this.lvIsRange) {
      const newValue = this.initRangeDate(value);
      this.selectedValue = [
        newValue[0] && new StandardDate(newValue[0]).clone(),
        newValue[1] && new StandardDate(newValue[1]).clone(),
      ];
    } else {
      this.date = this.initDateObject(value);
    }

    this._cdr.markForCheck();
  }

  doRangeValueChange(value, isPreset?): void {
    if (this.lvCurrentInput === 'start') {
      this.startDate = value.clone().localDate;
    } else if (this.lvCurrentInput === 'end') {
      this.endDate = value.clone().localDate;
    }

    this.resetActiveData();

    const newValue = [
      this.startDate && new StandardDate(this.startDate).clone(),
      this.endDate && new StandardDate(this.endDate).clone(),
    ];
    this.selectedValue = newValue;
    this._onChange([newValue[0]?.localDate || null, newValue[1]?.localDate || null]);

    this.handlePanelClosed(isPreset);
  }

  activeDateChange(e): void {
    if (this.lvIsRange) {
      if (!this.lvShowTime) {
        if (e.tag === 'start') {
          this.startActiveDate = e.value;
          this.endActiveDate = new StandardDate(this.startActiveDate.addMonths(1));
        } else if (e.tag === 'end') {
          this.endActiveDate = e.value;
          this.startActiveDate = new StandardDate(this.endActiveDate.addMonths(-1));
        }
      } else if (e.tag === 'start') {
        this.startActiveDate = e.value;
      } else if (e.tag === 'end') {
        this.endActiveDate = e.value;
      }

      this.lvActiveDateChange.emit([this.startActiveDate.localDate, this.endActiveDate.localDate]);
    } else {
      this.lvActiveDateChange.emit(e.value.localDate);
    }
  }

  onOk(): void {
    if (this.lvShowTime) {
      if (this.startDate && this.endDate) {
        if (this.startDate.getTime() > this.endDate.getTime()) {
          [this.startDate, this.endDate] = [this.endDate, this.startDate];
          [this.selectedValue[0], this.selectedValue[1]] = [this.selectedValue[1], this.selectedValue[0]];
          this._onChange(this.selectedValue.map((item): Date => item.localDate));
        }
        this.closeDateRangePicker();
      } else if (this.lvCurrentInput === 'start') {
        this.lvCurrentInput = 'end';
        this.lvCurrentInputChange.emit('end');
      } else {
        this.lvCurrentInput = 'start';
        this.lvCurrentInputChange.emit('start');
      }
    } else {
      this.closeDateRangePicker();
    }
  }

  isDisabled(date: StandardDate, tag: LvDateRangeTag, isPreset = false): boolean {
    let isDateDisabled = false;
    let isTimeDisabled = false;
    if (date && this.lvDisabledDate) {
      isDateDisabled = this.lvDisabledDate(date.localDate, tag, isPreset);
    }
    if (date && typeof this.lvShowTime !== 'boolean') {
      const hours = date.getHours();
      const minutes = date.getMinutes();
      const seconds = date.getSeconds();
      isTimeDisabled =
        this.lvShowTime.disabledHour?.(hours, tag) ||
        this.lvShowTime.disabledMinute?.(hours, minutes, tag) ||
        this.lvShowTime.disabledSecond?.(hours, minutes, seconds, tag);
    }
    return !date || isDateDisabled || isTimeDisabled;
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.lvCurrentInput) {
      const tag = changes.lvCurrentInput.currentValue;
      const isArray = this.selectedValue instanceof Array;
      if (!isArray) {
        return;
      }
      if (tag === 'start' && this.selectedValue[0] && this.selectedValue[1]) {
        this.startActiveDate = this.selectedValue[0] || new StandardDate(this.selectedValue[1].addMonths(-1));
        if (!this.lvShowTime) {
          this.endActiveDate = new StandardDate(this.selectedValue[0].addMonths(1));
        }
      } else if (tag === 'end' && this.selectedValue[1]) {
        if (this.lvShowTime) {
          this.endActiveDate = this.selectedValue[1] || this.startActiveDate;
        } else {
          this.startActiveDate = this.selectedValue[1] && new StandardDate(this.selectedValue[1].addMonths(-1));
          this.endActiveDate = this.selectedValue[1];
        }
      }
    }
  }

  initRangeDate(rangeDate): Date[] {
    if (rangeDate instanceof Array && (rangeDate[0] || rangeDate[1])) {
      this.startDate = rangeDate[0];
      this.endDate = rangeDate[1];
      const startStandardDate = new StandardDate(this.startDate);
      const endStandardDate = new StandardDate(this.endDate);

      // NEED_DO: 实现交互逻辑，后续优化代码，减少嵌套
      if (this.startDate && this.endDate) {
        if (this.lvShowTime) {
          this.startActiveDate = startStandardDate;
          if (startStandardDate.isCompareMonth(endStandardDate, 'less-than')) {
            this.endActiveDate = endStandardDate;
          } else {
            this.endActiveDate = startStandardDate;
          }
        } else if (this.lvCurrentInput === 'start') {
          this.startActiveDate = startStandardDate;
          this.endActiveDate = new StandardDate(startStandardDate.addMonths(1));
        } else {
          this.startActiveDate = new StandardDate(endStandardDate.addMonths(-1));
          this.endActiveDate = endStandardDate;
        }
      } else if (!this.startDate) {
        this.endActiveDate = endStandardDate;
        if (this.lvShowTime) {
          this.startActiveDate = endStandardDate;
        } else {
          this.startActiveDate = new StandardDate(endStandardDate.addMonths(-1));
        }
      } else if (!this.endDate) {
        this.startActiveDate = startStandardDate;
        if (this.lvShowTime) {
          this.endActiveDate = startStandardDate;
        } else {
          this.endActiveDate = new StandardDate(startStandardDate.addMonths(1));
        }
      }
    } else {
      this.startDate = null;
      this.endDate = null;
      this.startActiveDate = new StandardDate(new Date());
      if (this.lvShowTime) {
        this.endActiveDate = new StandardDate(new Date());
      } else {
        this.endActiveDate = new StandardDate(this.startActiveDate.addMonths(1));
      }
    }
    return [this.startDate, this.endDate];
  }

  private _onTouch: any = (): void => {};

  private _onChange: any = (): void => {};

  /**
   * 处理无时间的选择面板是否关闭
   */
  private handlePanelClosed(isPreset): void {
    if (this.lvShowTime || isPreset) {
      return;
    }

    if (this.lvCurrentInput === 'start') {
      if (this.selectedValue[1] && !this.checkDateDisableStatus('end')) {
        this.closeDateRangePicker();
      } else {
        this.lvCurrentInput = 'end';
        this.lvCurrentInputChange.emit('end');
      }
    } else if (this.lvCurrentInput === 'end') {
      if (this.selectedValue[0] && !this.checkDateDisableStatus('start')) {
        this.closeDateRangePicker();
      } else {
        this.lvCurrentInput = 'start';
        this.lvCurrentInputChange.emit('start');
      }
    }
  }

  /**
   * 重置日期窗口的activeDate
   */
  private resetActiveData(): void {
    if (this.lvShowTime) {
      if (this.lvCurrentInput === 'start') {
        this.startActiveDate = new StandardDate(this.startDate);
      } else if (this.lvCurrentInput === 'end') {
        this.endActiveDate = new StandardDate(this.endDate);
      }
    } else if (this.startDate) {
      this.startActiveDate = new StandardDate(this.startDate);
      if (this.endDate && this.startActiveDate.isCompareMonth(new StandardDate(this.endDate), 'less-than')) {
        this.endActiveDate = new StandardDate(this.endDate);
      } else {
        this.endActiveDate = new StandardDate(this.startActiveDate.addMonths(1));
      }
    } else if (this.endDate) {
      this.startActiveDate = new StandardDate(new StandardDate(this.endDate).addMonths(-1));
      this.endActiveDate = new StandardDate(this.endDate);
    } else {
      this.startActiveDate = new StandardDate(new Date());
      this.endActiveDate = new StandardDate(this.startActiveDate.addMonths(1));
    }
  }
}
